package com.supply;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * @author : wushikai
 * <p>
 * date : 2021/8/7
 */
public class DynamicLoadDemo {
    enum FileType {
        JAR, CLASS, OTHER
    }



    static class MyClassLoader extends ClassLoader {


        public synchronized Class<?> loadClass(String name, File file) throws FileNotFoundException {
            Class<?> cls = findLoadedClass(name);
            if(cls != null) {
                return cls;
            }
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            try {
                while (true) {
                    len = fis.read(buffer);
                    if (len == -1) {
                        break;
                    }
                    baos.write(buffer, 0, len);
                }
                //FileInputStream的flush是空操作，因为flush的作用是把缓存中的东西写入实体(硬盘或网络流)中，这里没有这种必要所以为空
                //baos.flush();
                byte[] data = baos.toByteArray();
                return defineClass(null, data, 0, data.length);
            }
            catch (IOException e) {
                e.printStackTrace();
            }
            finally {
                try {
                    baos.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    fis.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }
    public static void main(String[] args) {
        String className = "com.sky.Calc";
        String paths[] = { "jar_mechanism-1.0-SNAPSHOT.jar", "Calc.class" };
        for (String path : paths) {
            String lowerPath = path.toLowerCase();
            FileType fileType = FileType.OTHER;
            if (lowerPath.endsWith(".jar") || lowerPath.endsWith(".zip")) {
                fileType = FileType.JAR;
            } else if (lowerPath.endsWith(".class")) {
                fileType = FileType.CLASS;
            }
            if (fileType == FileType.OTHER) {
                return;
            }
            File file = new File(path);
            if (!file.exists()) {
                return;
            }
            try {
                URL url = file.toURI().toURL();
                System.out.println(url.toString());
                Class<?> cls = null;
                switch (fileType) {
                    case JAR:
                        URLClassLoader classLoader = new URLClassLoader(new URL[] { url }, Thread.currentThread().getContextClassLoader());
                        cls = classLoader.loadClass(className);
                        break;
                    case CLASS:
                        MyClassLoader myClassLoader = new MyClassLoader();
                        cls = myClassLoader.loadClass(className, file);
                        break;
                    default:
                        break;
                }
                if (cls == null) {
                    return;
                }
                // 实例变量
                Field field = cls.getDeclaredField("hello");
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                System.out.println(field.get(cls.newInstance()));
                // 调用静态不带参数方法
                Method staticMethod = cls.getDeclaredMethod("main", String.class);
                if (!staticMethod.isAccessible()) {
                    staticMethod.setAccessible(true);
                }
                // 如果函数的返回值是void，就会返回null
                staticMethod.invoke(cls, null);
                // 实例带参数方法方法
                Method method = cls.getDeclaredMethod("main", String.class);
                if (!method.isAccessible()) {
                    method.setAccessible(true);
                }
                Object ret = method.invoke(cls.newInstance(), "hello world");
                System.out.println(ret);
            }
            catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
}
